#include "dfize.h"
#include "CBlockCollection.h"
#include "CBlockSceneNode.h"
#include "CPickSceneNode.h"
#include "CSettings.h"

SColor colorFromMatGloss(DFHack::t_matgloss matgloss);
SColor colorFromMatGloss(u32 f, u32 b, u32 g);

ITexture* getColorTexture(IVideoDriver *video, const SColor color);

struct SShader
{
	SShader(std::string name, u32 materialtype)
		: Name(name), MaterialType(materialtype)
	{
	}
	std::string Name;
	u32 MaterialType;
};

struct SBlocks
{
	CBlockSceneNode *Blocks[9];
};

struct SBasicMaterial
{
public:
	SBasicMaterial(u32 index, std::string name, SMaterial m)
		: Index(index), Name(name), Material(m), Texture(0)
	{
	}
	SBasicMaterial(SMaterial material, IVideoDriver *video, u32 index, std::string name, DFHack::t_matgloss matgloss)
		: Index(index), Name(name), MatGloss(matgloss)
	{
		//printf("%d %d %d\n", MatGloss.fore, MatGloss.back, MatGloss.bright);
	//	printf("%d\n", getMatGlossColor());

		Material = material;
	//	Material.setFlag(video::EMF_ANISOTROPIC_FILTER, true);
	//	Material.AmbientColor = SColor(255, 64, 64, 64);
//		if(!Material.getTexture(0))
	//		Material.setTexture(0, getColorTexture(video, colorFromMatGloss(MatGloss)));
		Material.DiffuseColor = colorFromMatGloss(MatGloss);
	//	Material.ColorMaterial = video::ECM_NONE;

		//Material.MaterialType = video::EMT_DETAIL_MAP;
		//Material.MaterialType = (E_MATERIAL_TYPE)24;
	}

	u8 getMatGlossColor() const { if(MatGloss.bright) return MatGloss.fore + 8; return MatGloss.fore; }

	u32 Index;
	std::string Name;
	SMaterial Material;
	DFHack::t_matgloss MatGloss;
	ITexture *Texture;
};

struct SResource
{
public:
	SResource(std::string name, u16 tiletype, IMesh *mesh, ITexture *texture, /*SMaterial material, */vector3df position=vector3df(0,0,0), vector3df rotation=vector3df(0,0,0), vector3df scale=vector3df(1,1,1))
		: TileTypeMin(tiletype), TileTypeMax(tiletype), Mesh(mesh), Texture(texture), /*Material(material), */Position(position), Rotation(rotation), Scale(scale), Name(name)
	{
		if(Mesh)
		{
		//	Mesh->setHardwareMappingHint(scene::EHM_STATIC);
			Box = Mesh->getBoundingBox();
		}
	}
	SResource(std::string name, u16 tiletypemin, u16 tiletypemax, IMesh *mesh, ITexture *texture, /*SMaterial material, */vector3df position=vector3df(0,0,0), vector3df rotation=vector3df(0,0,0), vector3df scale=vector3df(1,1,1))
		: TileTypeMin(tiletypemin), TileTypeMax(tiletypemax), Mesh(mesh), Texture(texture), /*Material(material), */Position(position), Rotation(rotation), Scale(scale), Name(name)
	{
		if(Mesh)
		{
		//	Mesh->setHardwareMappingHint(scene::EHM_STATIC);
			Box = Mesh->getBoundingBox();
		}
	}

	SResource(std::string name, IMesh *mesh, ITexture *texture, vector3df position=vector3df(0,0,0), vector3df rotation=vector3df(0,0,0), vector3df scale=vector3df(1,1,1))
		: TileTypeMin(0), TileTypeMax(0), Mesh(mesh), Texture(texture), Position(position), Rotation(rotation), Scale(scale), Name(name)
	{
		if(Mesh)
		{
		//	Mesh->setHardwareMappingHint(scene::EHM_STATIC);
			Box = Mesh->getBoundingBox();
		}
	}

	matrix4 getTransformation()
	{
		matrix4 mat;
		mat.setRotationDegrees(Rotation);
		mat.setTranslation(Position);
		if(Scale != vector3df(1.f,1.f,1.f))
		{
			matrix4 smat;
			smat.setScale(Scale);
			mat *= smat;
		}
		return mat;
	}

	std::string Name;
	u16 TileTypeMin, TileTypeMax;
//	SMaterial Material;
	IMesh *Mesh;
	ITexture *Texture;
	aabbox3df Box;
	vector3df Position, Rotation, Scale;
};

class CRegionSceneNode : public ISceneNode
{
public:
	CRegionSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id=-1, vector3di size=vector3di(10,10, 10),
				const core::vector3df& position = core::vector3df(0,0,0),
				const core::vector3df& rotation = core::vector3df(0,0,0),
				const core::vector3df& scale = core::vector3df(1.0f, 1.0f, 1.0f));
	~CRegionSceneNode();

	void preRender();
	virtual void render();

	virtual const core::aabbox3d<f32>& getBoundingBox() const
    {
		return BoundingBox;
    }

    virtual u32 getMaterialCount() const
    {
		return 0;
    }

	void OnAnimate(u32 timeMs)
	{
		if(IsVisible)
		{
			updateAbsolutePosition();
		}
	}

    /*virtual video::SMaterial& getMaterial(u32 i)
    {
            return Material;
    }*/

	virtual const u32 getVisibleBlockCount() const
	{
		return BlocksToRender.size();
	}

	virtual const u32 getBlockCount() const
	{
		return BlockCount;
	}

	virtual CBlockSceneNode* getBlock(s32 x, s32 y, s32 z) const;
	virtual CBlockSceneNode* getBlock(vector3df pos) const;
	virtual CBlockSceneNode* getBlock(u32 i)
	{
		return Blocks[i];
	}
//	virtual STile* getTile(u32 x, u32 y, u32 z);

	virtual void OnRegisterSceneNode()
	{
		if (IsVisible)
		{
//			SceneManager->registerNodeForRendering(this);
			preRender();
	//		ISceneNode::OnRegisterSceneNode();
		}
	}

	void resetBlocks();
	void updateBlocks(bool reset=false);
	SBlocks getSurroundingBlocks(vector3df pos);

	void removeBlock(ISceneNode *block);

	void removeTile(u32 i);

	virtual array<CBlockSceneNode*> getBlocksToRender()
	{
		return BlocksToRender;
	}

	ISceneNode* getDummyNode()
	{
		return DummyNode;
	}

	void setForceRecalculation(bool force) { ForceRecalculation = force; }

	SResource* getResource(u16 tiletype) const;
	SResource* getResource(std::string name) const;
	void addResource(SResource *resource);

	array<CBlockSceneNode*> getBlocksFromLine(line3df line, bool sort=true);
	CTile* getTileFromLine(line3df line);

	array<u16>& getTriangleIndices() { return TriangleIndices; }
	array<S3DVertex>& getTriangleVertices() { return TriangleVertices; }

	bool getWireframe() { return Wireframe; }
	void setWireframe(bool wireframe) { Wireframe = wireframe; }

	CTile* getTile(u32 gx, u32 gy, u32 gz) const;
	CTile* getTile(vector3di p) const;

	const vector3di getSize() const { return Size; }

	void updateTiles();

	void showTerrain(bool show) { ShowTerrain = show; }

	void setDrawDistance(f32 distance) { DrawDistance = distance; }
	const f32 getDrawDistance() const { return DrawDistance; }

	SBasicMaterial* getInorganicMaterial(u32 index) const;
	void AddInorganicMaterial(SBasicMaterial *m);
	const u32 getInorganicMaterialCount() const { return InorganicMaterials.size(); }

	SBasicMaterial* getOrganicMaterial(u32 index) const;
	void AddOrganicMaterial(SBasicMaterial *m);
	const u32 getOrganicMaterialCount() const { return OrganicMaterials.size(); }

	SBasicMaterial* getPlantMaterial(u32 index) const;
	void AddPlantMaterial(SBasicMaterial *m);
	const u32 getPlantMaterialCount() const { return PlantMaterials.size(); }

	SBasicMaterial* getWoodMaterial(u32 index) const;
	void AddWoodMaterial(SBasicMaterial *m);
	const u32 getWoodMaterialCount() const { return WoodMaterials.size(); }

	SBasicMaterial* getOtherMaterial(u32 index) const;
	void AddOtherMaterial(SBasicMaterial *m);
	const u32 getOtherMaterialCount() const { return OtherMaterials.size(); }

	SBasicMaterial* getMaterial(u32 index, u32 type) const;

	SMaterial readMaterial(std::string name);

	bool getHq() const { return Hq; }
	void setHq(bool hq) { Hq = hq; }

	u32 getTileCount() const;
	u32 getHiddenTileCount() const;
	u32 getCurrentHiddenTileCount() const;
	u32 getCurrentTotalTileCount() const;

	u32 getBlockCollectionCount() const { return BlockCollections.size(); }
	CBlockCollection* getBlockCollection(u32 i);

	void setMaxUp(f32 max) { MaxUp = max; }
	void setMaxDown(f32 max) { MaxDown = max; }

	void setUseBatching(bool batch) { UseBatching = batch; }
	bool getUseBatching() const { return UseBatching; }

	SShader* getShaderMaterial(std::string name) const;
	void addShaderMaterial(std::string name, u32 materialtype);

private:
	vector3di Size;
	//core::map<vector3di, ISceneNode*> Blocks;
	aabbox3d<f32> BoundingBox;
	IMesh *BlockMesh;
//	CDynamicMeshBuffer *MeshBuffer;
//	CVertexBuffer *VertexBuffer;
	//ISceneNode **Blocks;
//	SBlock *Blocks;
	u32 BlockCount;
	u32 VisibleBlocks;
	CBlockSceneNode **Blocks;

//	list<u32> BlocksToRender;
	//std::vector<u32> BlocksToRender;
//	std::vector<CBlockSceneNode*> BlocksToRender;
	array<CBlockSceneNode*> BlocksToRender;
	array<CBlockCollection*> BlockCollections;

	array<SShader*> ShaderMaterials;

	vector3df OldCameraPosition;
	vector3df OldCameraRotation;
	vector3df OldCameraUp;
	f32 OldCameraFOV, CameraMovementDelta, CameraRotationDelta, CameraFOVDelta;
	bool ForceRecalculation, Wireframe, ShowTerrain;

	CBlockSceneNode *CurrentBlock;
	vector3df CurrentPosition;

	ISceneNode *DummyNode;

	array<u16> TriangleIndices;
	array<S3DVertex> TriangleVertices;

	array<SResource*> Resources;
	array<SBasicMaterial*> InorganicMaterials, OrganicMaterials, WoodMaterials, OtherMaterials, PlantMaterials;

	f32 DrawDistance;

	f32 MaxUp, MaxDown;

	bool Hq, UseBatching;
};